#include "String.h"

BASE(String, None, 100)

static char EMPTY_PCHAR[1] = {0};

//HIDDEN
static char *pcharCopy(const char *src, unsigned int len)
{
	if(!len) return EMPTY_PCHAR;

	char *ptr = (char *)malloc(sizeof(char)*(len+1));

	if(ptr)
	{
		memcpy(ptr, src, sizeof(char)*len);
		ptr[len]=0;
	}

	return ptr;
}

static char *pcharCat(const char *ptr1, int len1, const char *ptr2, int len2)
{
	int len = len1 + len2;

	if(!len) return EMPTY_PCHAR;

	char *dst = (char *)malloc(sizeof(char)*(len+1));

	if(dst)
	{
		int i;
		int j=0;

		for(i=0;i<len1;i++)
		{
			dst[j++] = ptr1[i];
		}

		for(i=0;i<len2;i++)
		{
			dst[j++] = ptr2[i];
		}
		dst[j] = 0;
	}

	return dst;
}

static String *append(const char *ptr1, int len1, const char *ptr2, int len2)
{
	String *result = I()->create();

	if(result)
	{
		result->ptr = pcharCat(ptr1,len1,ptr2,len2);
		if(!result->ptr)
		{
			D(result);
			return 0;
		}
		result->length = len1+len2;
	}

	return result;
}

static void freePtr(char *ptr)
{
	if(ptr && ptr!=EMPTY_PCHAR)
	{
		free(ptr);
	}
}

//SHARED
IMPL(int, init)
{
	THIS->ptr = EMPTY_PCHAR;
	return 1;
}

IMPL(void, destroy)
{
	freePtr(THIS->ptr);
}

IMPL(int, setPChar, const char *ptr)
{
	unsigned int len=0;
	if(ptr) len = strlen(ptr);

	char *copy = pcharCopy(ptr, len);
	if(!copy) return 0;

	freePtr(THIS->ptr);
	THIS->ptr = copy;
	THIS->length=len;

	return 1;
}

IMPL(int, setNPChar, const char *ptr, unsigned int len)
{
	if(ptr)
	{
		unsigned int realLen = strlen(ptr);
		if(len>realLen) len = realLen;
	}
	else
	{
		len = 0;
	}

	char *copy = pcharCopy(ptr, len);
	if(!copy) return 0;

	freePtr(THIS->ptr);
	THIS->ptr = copy;
	THIS->length=len;

	return 1;
}

IMPL(int, setString, String *str)
{
	unsigned int len=0;
	if(str) len = str->length;

	char *copy = pcharCopy(str->ptr, len);
	if(!copy) return 0;

	freePtr(THIS->ptr);
	THIS->ptr = copy;
	THIS->length=len;

	return 1;
}

IMPL(char *, pchar)
{
	return THIS->ptr;
}

IMPL(int, isEmpty)
{
	return !THIS->length;
}

IMPL(String *, substring, int from, int to)
{
	if(from<0) from = 0;
	if(from>=THIS->length) from = THIS->length-1;

	if(to<0) to = 0;
	if(to>=THIS->length) to = THIS->length-1;

	String *result = I()->create();
	if(!result) return 0;

	if(from<=to)
	{
		int length = (to-from)+1;
		result->ptr = pcharCopy(THIS->ptr+from, length);
		if(!result->ptr)
		{
			D(result);
			return 0;
		}
		result->length = length;
	}

	return result;
}

IMPL(String *, trim)
{
	int from = 0;
	while( THIS->ptr[from] && (iscntrl(THIS->ptr[from]) || isspace(THIS->ptr[from])) ) ++from;

	int to = THIS->length;
	while( (to>from) && (iscntrl(THIS->ptr[to]) || isspace(THIS->ptr[to])) ) --to;

	return C(substring)(from,to);
}

IMPL(String *, pre, unsigned int len)
{
	return C(substring)(0,len-1);
}

IMPL(String *, pos, unsigned int len)
{
	return C(substring)(THIS->length-1-(len-1),THIS->length-1);
}

IMPL(int, startsPChar, const char *ptr)
{
	if(!ptr) return 0;

	unsigned int len = strlen(ptr);
	if(len>THIS->length) return 0;

	return !strncmp(ptr, THIS->ptr, len);
}

IMPL(int, startsString, String *str)
{
	if(!str) return 0;

	unsigned int len = str->length;
	if(len>THIS->length) return 0;

	return !strncmp(str->ptr, THIS->ptr, len);
}

IMPL(int, startsPCharIgnoreCase, const char *ptr)
{
	if(!ptr) return 0;

	unsigned int len = strlen(ptr);
	if(len>THIS->length) return 0;

	return !strncasecmp(ptr, THIS->ptr, len);
}

IMPL(int, startsStringIgnoreCase, String *str)
{
	if(!str) return 0;

	unsigned int len = str->length;
	if(len>THIS->length) return 0;

	return !strncasecmp(str->ptr, THIS->ptr, len);
}

IMPL(int, endsPChar, const char *ptr)
{
	if(!ptr) return 0;

	unsigned int len = strlen(ptr);
	if(len>THIS->length) return 0;

	char *thisPtr = &THIS->ptr[THIS->length-len];

	return !strncmp(ptr, thisPtr, len);
}

IMPL(int, endsString, String *str)
{
	if(!str) return 0;

	unsigned int len = str->length;
	if(len>THIS->length) return 0;

	char *thisPtr = &THIS->ptr[THIS->length-len];

	return !strncmp(str->ptr, thisPtr, len);
}

IMPL(int, endsPCharIgnoreCase, const char *ptr)
{
	if(!ptr) return 0;

	unsigned int len = strlen(ptr);
	if(len>THIS->length) return 0;

	char *thisPtr = &THIS->ptr[THIS->length-len];

	return !strncasecmp(ptr, thisPtr, len);
}

IMPL(int, endsStringIgnoreCase, String *str)
{
	if(!str) return 0;

	unsigned int len = str->length;
	if(len>THIS->length) return 0;

	char *thisPtr = &THIS->ptr[THIS->length-len];

	return !strncasecmp(str->ptr, thisPtr, len);
}

IMPL(int, findPChar, const char *ptr)
{
	if(!ptr) return -1;

	char *p = strstr(THIS->ptr,ptr);
	if(!p) return -1;

	return (p-THIS->ptr);
}

IMPL(int, findString, String *str)
{
	if(!str) return -1;

	return C(findPChar)(str->ptr);
}

IMPL(String *, toLow)
{
	String *lowStr = I()->create();
	if(!lowStr) return 0;

	if(!C(lowStr,setPChar)(THIS->ptr))
	{
		D(lowStr);
		return 0;
	}

	int i;
	for(i=0; i<lowStr->length; i++)
	{
		lowStr->ptr[i] = tolower(lowStr->ptr[i]);
	}

	return lowStr;
}

IMPL(String *, toUp)
{
	String *upStr = I()->create();
	if(!upStr) return 0;

	if(!C(upStr,setPChar)(THIS->ptr))
	{
		D(upStr);
		return 0;
	}

	int i;
	for(i=0; i<upStr->length; i++)
	{
		upStr->ptr[i] = toupper(upStr->ptr[i]);
	}

	return upStr;
}

IMPL(String *, appendPChar, const char *ptr)
{
	int len = 0;

	if(ptr)
	{
		len = strlen(ptr);
	}

	return append(THIS->ptr,THIS->length,ptr,len);
}

IMPL(String *, appendString, String *str)
{
	int len = 0;
	char *ptr = 0;

	if(str)
	{
		len = str->length;
		ptr = str->ptr;
	}

	return append(THIS->ptr,THIS->length,ptr,len);
}

IMPL(String *, prependPChar, const char *ptr)
{
	int len = 0;

	if(ptr)
	{
		len = strlen(ptr);
	}

	return append(ptr,len,THIS->ptr,THIS->length);
}

IMPL(String *, prependString, String *str)
{
	int len = 0;
	char *ptr = 0;

	if(str)
	{
		len = str->length;
		ptr = str->ptr;
	}

	return append(ptr,len,THIS->ptr,THIS->length);
}

IMPL(int, compareTo, String *other)
{
	if(!other) return 1;
	if(THIS == other) return 0;

	return strcmp(THIS->ptr,other->ptr);
}

IMPL(int, compareToPChar, const char *ptr)
{
	if(!ptr) return 1;

	return strcmp(THIS->ptr,ptr);
}

IMPL(int, compareToIgnoreCase, String *str)
{
	if(!str) return 1;
	if(THIS == str) return 0;

	return strcasecmp(THIS->ptr,str->ptr);
}

IMPL(int, compareToPCharIgnoreCase, const char *ptr)
{
	if(!ptr) return 1;

	return strcasecmp(THIS->ptr,ptr);
}

IMPL(String *, replace, const char *from, const char *to)
{
	String *result = I()->create();
	if(!result) return 0;

	if(!from || !to)
	{
		if(!C(result,setString)(THIS)) goto ERROR;
		return result;
	}

	char *tmp = strstr(THIS->ptr, from);
	if(!tmp)
	{
		if(!C(result,setString)(THIS)) goto ERROR;
		return result;
	}

	int pos = tmp - THIS->ptr;
	int lenFrom = strlen(from);
	int lenTo = strlen(to);
	int len = THIS->length - (lenFrom - lenTo);

	char *ptr = (char *)malloc(sizeof(char)*(len+1));
	if(!ptr) goto ERROR;

	memcpy(ptr, THIS->ptr, sizeof(char)*(pos));
	memcpy(ptr+pos, to, sizeof(char)*lenTo);
	memcpy(ptr+pos+lenTo, THIS->ptr+pos+lenFrom, sizeof(char) * (len-(pos+lenTo)) );
	ptr[len]=0;
	result->ptr = ptr;
	result->length=len;

	return result;

ERROR:
	D(result);
	return 0;
}

IMPL(String *, replaceAll, const char *from, const char *to)
{
	String *result = I()->create();
	if(!result) return 0;

	if(!from || !to)
	{
		if(!C(result,setString)(THIS)) goto ERROR;
		return result;
	}

	char *tmp = THIS->ptr;
	int num = 0;
	int lenFrom = strlen(from);

	while( (tmp=strstr(tmp, from)) )
	{
		tmp += lenFrom;
		++num;
	}

	if(!num)
	{
		if(!C(result,setString)(THIS)) goto ERROR;
		return result;
	}

	int lenTo = strlen(to);
	int len = THIS->length - num*(lenFrom-lenTo);

	char *ptr = (char *)malloc(sizeof(char)*(len+1));
	if(!ptr) goto ERROR;

	char *dst = ptr;

	tmp = THIS->ptr;
	char *end;

	while( (end=strstr(tmp, from)) )
	{
		int sz = end-tmp;
		memcpy(dst, tmp, sizeof(char)*sz);
		dst+=sz;
		memcpy(dst, to, sizeof(char)*lenTo);
		dst+=lenTo;
		tmp = end+lenFrom;
	}

	int sz = strlen(tmp);
	memcpy(dst, tmp, sizeof(char)*sz);
	ptr[len]=0;
	result->ptr=ptr;
	result->length=len;

	return result;

ERROR:
	D(result);
	return 0;
}

IMPL(unsigned int, hash)
{
	return _pcharHash(THIS->ptr);
}

IMPL(unsigned int, length)
{
	return THIS->length;
}

SETUP_IF(ENTRY(init), ENTRY(destroy),
ENTRY(setPChar), ENTRY(setNPChar), ENTRY(setString),
ENTRY(pchar), ENTRY(isEmpty),
ENTRY(substring), ENTRY(trim), ENTRY(pre), ENTRY(pos),
ENTRY(startsPChar), ENTRY(startsString),
ENTRY(startsPCharIgnoreCase), ENTRY(startsStringIgnoreCase),
ENTRY(endsPChar), ENTRY(endsString),
ENTRY(endsPCharIgnoreCase), ENTRY(endsStringIgnoreCase),
ENTRY(findPChar), ENTRY(findString),
ENTRY(toLow), ENTRY(toUp),
ENTRY(appendPChar), ENTRY(appendString),
ENTRY(prependPChar), ENTRY(prependString),
ENTRY(compareTo), ENTRY(compareToPChar),
ENTRY(compareToIgnoreCase), ENTRY(compareToPCharIgnoreCase),
ENTRY(replace), ENTRY(replaceAll),
ENTRY(hash), ENTRY(length) )
